package com.vcc.core.util;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.vcc.core.exception.ProgramException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis 工具类
 *
 * @author Lion Li
 * @version 3.1.0 新增
 */
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public class RedisCustomizeUtils {

    private static final StringRedisTemplate redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);


    public static boolean set(String key, Object value, Long expire, TimeUnit timeUnit) {
        try {
            if (value instanceof String) {
                redisTemplate.opsForValue().set(key, (String) value, expire, timeUnit);
            } else {
                redisTemplate.opsForValue().set(key, JSON.toJSONString(value), expire, timeUnit);
            }
            return true;
        } catch (Exception e) {
            log.error("当前为redis写入String数据,异常信息：{}", e.getMessage());
            return false;
        }
    }

    public static boolean set(String key, Object value) {
        try {
            if (value instanceof String) {
                redisTemplate.opsForValue().set(key, (String) value);
            } else {
                redisTemplate.opsForValue().set(key, JSON.toJSONString(value));
            }
            return true;
        } catch (Exception e) {
            log.error("当前为redis写入String数据,异常信息：{}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取制定key的内容，没有返回null
     *
     * @param key 键
     */
    public static String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 获取指定key的内容，并且转换成对应的类型
     *
     * @param key           键
     * @param typeReference 类型
     */
    public static <T> T get(String key, TypeReference<T> typeReference) {
        try {
            String value = get(key);
            if (StringUtils.isNotBlank(value)) {
                return JSONObject.parseObject(value, typeReference);
            }
            throw new ProgramException("获取数据key:" + key + ",value:" + value);
        } catch (Exception e) {
            log.error("当前为获取数据异常：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取指定key的内容，并且转换成对应的类型
     *
     * @param key   键
     * @param clazz 类型
     */
    public static <T> T get(String key, Class<T> clazz) {
        try {
            String value = get(key);
            if (StringUtils.isNotBlank(value)) {
                return JSONObject.parseObject(value, clazz);
            }
            throw new ProgramException("获取数据key:" + key + ",value:" + value);
        } catch (Exception e) {
            log.error("当前为获取数据异常：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取指定key的内容，并且转换成对应的类型
     *
     * @param key   键
     * @param clazz 类型
     */
    public static <T> List<T> getList(String key, Class<T> clazz) {
        try {
            String value = get(key);
            if (StringUtils.isNotBlank(value)) {
                return JSONUtil.toList(value, clazz);
            }
            throw new ProgramException("获取数据key:" + key + ",value:" + value);
        } catch (Exception e) {
            log.error("当前为获取数据异常：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取指定key的内容，并且转换List数组
     *
     * @param key    键
     * @param slicer 分隔符
     */
    public static List<String> get(String key, String slicer) {
        try {
            String value = get(key);
            if (StringUtils.isNotBlank(value)) {
                return Arrays.asList(value.split(slicer));
            }
            throw new ProgramException("获取数据key:" + key + ",value:" + value);
        } catch (Exception e) {
            log.error("当前为获取数据异常：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 判断某个键值对是否存在
     *
     * @param key 键
     */
    public static Boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 删除指定元素
     *
     * @param key 键
     */
    public static Boolean removeKey(String key) {
        log.info("删除指定key:{}", key);
        return redisTemplate.delete(key);
    }

    /**
     * 删除指定元素
     *
     * @param prefixKey 键
     */
    public static Boolean removePrefixKey(String prefixKey) {
        log.info("删除指定前缀key:{}", prefixKey);
        Set<String> keys = redisTemplate.keys(prefixKey + "*");
        log.info("删除指定前缀key,查询到的key数量为:{}", keys.size());
        redisTemplate.delete(keys);
        return true;
    }

    /**
     * 批量删除key
     *
     * @param keys 键值集合
     * @return 是否删除成功
     */
    public static Boolean removeKeys(List<String> keys) {
        try {
            //先过滤掉不存在的缓存
            keys = keys.stream().filter(RedisCustomizeUtils::hasKey).collect(Collectors.toList());
            //删除行数
            Long removeNum = Optional.ofNullable(redisTemplate.delete(keys)).orElse(0L);
            //如果删除数等于传递进来的list大小，则返回正常
            if (removeNum != keys.size()) {
                //判断key存不存在
                removeKeys(keys);
            }
            return true;
        } catch (Exception e) {
            log.error("传递参数为:{},异常信息为:{}", keys, e.getMessage());
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key   键
     * @param value 要增加几(大于0)
     */
    public static Long increase(String key, Long value, Long time) {
        if (value < 0) {
            throw new ProgramException("递增因子必须大于0");
        }
        Long increment = redisTemplate.opsForValue().increment(key, value);
        expire(key, time);
        return increment;
    }

    /**
     * 递减
     *
     * @param key   键
     * @param value 要减少几(大于0)
     */
    public static Long decrease(String key, Long value, Long time) {
        if (value < 0) {
            throw new ProgramException("递增因子必须大于0");
        }
        Long increment = redisTemplate.opsForValue().increment(key, -value);
        expire(key, time);
        return increment;
    }


//    public static boolean listSet( String key,  List<Object> value,  Long expire,  TimeUnit timeUnit) {
//        try {
//            redisTemplate.opsForList().set(key, );
//            return true;
//        } catch (Exception e) {
//            log.error("当前为redis写入String数据,异常信息：{}", e.getMessage());
//            return false;
//        }
//    }


    public static Boolean expire(String key, Long time) {
        return expire(key, time, TimeUnit.SECONDS);
    }

    public static Boolean expire(String key, Long time, TimeUnit timeUnit) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, timeUnit);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取匹配规则的key
     *
     * @param pattern 不完整的一个key
     * @return 满足条件的key集合
     */
    public static Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    public static Boolean setIfAbsent(String key, Object value, Long expire, TimeUnit timeUnit) {
        try {
            if (value instanceof String) {
                return redisTemplate.opsForValue().setIfAbsent(key, (String) value, expire, timeUnit);
            } else {
                return redisTemplate.opsForValue().setIfAbsent(key, JSON.toJSONString(value), expire, timeUnit);
            }
        } catch (Exception e) {
            log.error("当前为redis写入String数据,异常信息：{}", e.getMessage());
            return false;
        }
    }

    /**
     * 设置一个key，如果不存在则设置成功并返回true，存在则不做任何操作返回false
     * 默认5分钟超时时间
     *
     * @param key 键
     * @return 如果设置成功返回true，否则返回false
     */
    public static Boolean setIfAbsent(String key) {
        return setIfAbsent(key, "", 5L, TimeUnit.MINUTES);
    }

    public static Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
}
